<?php
	require_once('./connect_db.php');
	require_once('./headers/data.php');
	require_once('./headers/partit.php');
	require_once('./headers/participant.php');
	require_once('./headers/equip.php');
	require_once('./headers/grup.php');
	require_once('./headers/partit.php');
	require_once('./headers/pista.php');
		
	define('TEMPS_REPOS', 2);							// Temps que es considera necessari per reposar
		
    class horari{
    	// Horari final
    	private $taulaHorari;	// 3D [dia][pista][hora]
		
		// Dades per calcular la llista de partits
		private $grups;
		private $pistes;
		private $participants;
		private $equips;
		private $categories;
		
		// INFO
		private $n_dies;
		private $n_pistes;
		private $hores_torneig;
		private $partits_hora;
		private $max_eq_grup;
		private $min_eq_grup;
		
		public function __construct(){
			$db = new bd();
			//$query="Select * From usuaris Where Login ='$Login' AND Password ='$Password'";

			// Descarregar valors info
	
			// Obtenim la resta de la informació general
			// PRE: info esta correctament inicialitzat
			$query="Select * From info";
			$reg=$db->consulta($query);	
			$this->n_dies = $reg[0]['n_dies'];
			$this->n_pistes = $reg[0]['n_pistes'];
			$this->partits_hora = $reg[0]['partits_hora'];
			$this->max_eq_grup = $reg[0]['max_eq_grup'];
			$this->min_eq_grup = $reg[0]['min_eq_grup'];
	

			// Obtenim les hores que es juga cada dia
			// PRE: hi ha el numero just de instancies per dies de torneig, i estan ordenats
			$query="Select * From hores_dies";
			$hores=$db->consulta($query);
			for ($i=0; $i < $this->n_dies; $i++) { 
				$this->hores_torneig[$i] = new data();
				$this->hores_torneig[$i]->setData($i,$hores[$i]['h_ini'], $hores[$i]['h_fi']);
			}
			
			// Fixem mides del horari final
			$this->taulaHorari = array();
			for ($i=0; $i < $this->n_dies; $i++) { 
				$this->taulaHorari[$i] = array();
				for ($j=0; $j < $this->n_pistes; $j++) {
					$aux = $this->hores_torneig[$i]->getTotalHores()*$this->partits_hora; 

					$this->taulaHorari[$i][$j] = array();
					
					for ($k=0; $k < $aux; $k++) { 
						$p = new partit();
						$p->setId(-1);
						$this->taulaHorari[$i][$j][$k] = $p;
					}
					
				}
			}

			// CATEGORIES		(de moment no necessari) PENSAR SI FER-LA ASSOCIATIVA ¿? (sense $i, amb 'nom')
			
			$query="Select * From categories Order By nom";
			$c=$db->consulta($query);
			
			
			for ($i=0; $i < sizeof($c); $i++) { 
				$this->categories[$c[$i]['nom']]['min_part'] = $c[$i]['min_part'];
				$this->categories[$c[$i]['nom']]['sexes'] = $c[$i]['sexes'];
				$this->categories[$c[$i]['nom']]['nivell'] = $c[$i]['nivell'];
			}
			
			// PISTES
            $this->pistes = array();
			$query="Select * From pistes Order By nom";
			$p=$db->consulta($query);
			for ($i=0; $i < $this->n_pistes; $i++) {
				$this->pistes[$i] = new pista();
				$this->pistes[$i]->setNom($p[$i]['nom']);
				$this->pistes[$i]->setId($i);
			}
			// CATEGORIA PISTES
			// Pre: Totes les pistes d'aquesta taula també han de ser-hi a la taula PISTES, sino bucle infinit!
			$query="Select * From cat_pista Order By nom";
			$cp=$db->consulta($query);
			$n_pistes_cats = sizeof($cp);
			$p_aux = 0;
			for ($i=0; $i < $n_pistes_cats; $i++) {
				while($this->pistes[$p_aux]->getNom() != $cp[$i]['nom'])	$p_aux++;
				$this->pistes[$p_aux]->addCat($cp[$i]['categoria']); 
			}

			// PARTICIPANTS
			$query="Select * From participants Order By dni";
			$parts=$db->consulta($query);
			$n_parts = sizeof($parts);
			for ($i=0; $i < $n_parts; $i++) { 
				$this->participants[$i] = new participant();
				$this->participants[$i]->setDadesBasic($parts[$i]['dni'], $parts[$i]['nom'],$parts[$i]['sexe'],$parts[$i]['nivell'], $this->n_dies);
			}
			
			// RESTRICCIONS
			// Pre: Tots els participants de restriccions, estan a la bd, i restriccions no repetides. SINO BUCLE INFINIT!
			// TODO quan es fagi eliminar participant, eliminar restriccions!!
			$query="Select * From restriccions Order By dni";
			$res=$db->consulta($query);
			
			$p_aux = 0;
			for ($i=0; $i < sizeof($res); $i++) { 
				while($this->participants[$p_aux]->getDni() != $res[$i]['dni'])	$p_aux = $p_aux+1;
				$this->participants[$p_aux]->setRestriccions($res[$i]['dia'],$res[$i]['mati'],$res[$i]['migdia'],$res[$i]['tarda']);
			}

			/* Funciona, però es pot fer més eficient si els noms estan ordenats
			for ($i=0; $i < sizeof($res); $i++) {
				$trobat = FALSE; 
				for ($j=0; $j < $n_parts && !$trobat; $j++) { 
					if($res[$i]['nom'] == $this->participants[$j]->getNom()){
						$this->participants[$j]->setRestriccions($res[$i]['dia'],$res[$i]['mati'],$res[$i]['migdia'],$res[$i]['tarda']);
						$trobat = TRUE;
					}
				}
			}*/
			
			// EQUIPS
			$query="Select * From equips Order By nom";
            /* FUNCIONA B� per� les restriccions es fan a l'entrar els participants a l'equip
            $query =   "Select e.nom, e.categoria, c.min_part, c.max_part
                        From equips e, categories c, equip_participants ep
                        Where c.nom = e.categoria and ep.nom_eq = e.nom
                        Group By e.nom
                        Having count(ep.dni_part) >= c.min_part and count(ep.dni_part) <= c.max_part";
            */
			$eq=$db->consulta($query);
			for ($i=0; $i < sizeof($eq); $i++) { 
				$this->equips[$i] = new equip();
				$this->equips[$i]->setDadesBasic($eq[$i]['nom'],$eq[$i]['categoria'], $this->n_dies);
			}
			
			
			// TODO faltarà agafar equips que tinguin un nombre minim de participants!
			//$query="Select nom_part From equip_participants Group By nom_eq";
			$query="Select * From equip_participants Order By nom_eq";
			$eq=$db->consulta($query);
			$p_aux = 0;
			for ($i=0; $i < sizeof($eq); $i++) {
				while($this->equips[$p_aux]->getNom() != $eq[$i]['nom_eq'])	$p_aux = $p_aux+1;
				$this->equips[$p_aux]->addParticipant($eq[$i]['dni_part']);
				$trobat = FALSE;
				for ($j=0; $j < $n_parts && !$trobat; $j++) { 
					if($this->participants[$j]->getDni() == $eq[$i]['dni_part']){
						$this->equips[$p_aux]->setRes($this->participants[$j]->getRes());
						$trobat = TRUE;
						
					}
				}
			}
			

		}
		
		public function generarGrups(){
			// A partir dels equips, les categories i la info, generem els grups
			$this->grups = array();
			$cats_aux = array();

			// $cats_aux mapeja una taula de categories amb els seus respectius equips
			$n_equips = sizeof($this->equips);
			for ($i=0; $i < $n_equips; $i++) {
				if($this->equips[$i]->getNumPart() >= $this->categories[$this->equips[$i]->getCat()]['min_part']) {
					$cats_aux[$this->equips[$i]->getCat()][] = $this->equips[$i];
				} else{
					// TODO Notificar al LOG que s'he eliminat un equip per falta de jugadors..
					
				}

			}

			foreach ($cats_aux as $cat => $eqs) {

				if(sizeof($cats_aux[$cat]) < $this->min_eq_grup)	{
					// TODO Avisar que no hi ha equips suficients!!! (Log?)
					unset($cats_aux[$cat]);
					
				} else {
					$n_grups = 1;
					
					$size = sizeof($cats_aux[$cat]);
					if($size > $this->max_eq_grup){
						if($size%$this->max_eq_grup == 0)	$n_grups = (int)$size/$this->max_eq_grup;
						else 								$n_grups = (int)($size/$this->max_eq_grup)+1;
					}	


					// Distribuim aleatoriament els equips en $n_grups diferents i ens inserim a $this->grups
					$mida_grups = sizeof($this->grups);
					
					
					for ($i=0; $i < $n_grups; $i++) { 
						$this->grups[] = new grup();
						$n = sizeof($this->grups);
						$this->grups[$n-1]->setDadesBasic("Grup $n", $cat);
					}
					
					$k = 0;
					for ($i=0; $i < $size; $i++) { 
						$equip_rand = $this->getEquipRand($cats_aux[$cat]);
						$this->grups[$mida_grups+$k]->addEquip($equip_rand);
						$k++;
						if($k == $n_grups)	$k = 0;
					}
					
				}
			}
			/*
					//XIVATO PER VEURE SI ELS GRUPS ES FORMEN BÉ
			for ($i=0; $i < sizeof($this->grups); $i++) { 
				echo "GRUP ".$i ."<br>";
				print_r($this->grups[$i]);
				echo "<br><br>";
			}
			*/
		}
		
		private function getEquipRand(array &$llista_equips){

			$r = rand(0, sizeof($llista_equips)-1);
			$ret = array_slice($llista_equips, $r, 1);		// $ret = $llista_equips[$r];
			
			unset($llista_equips[$r]);
			$llista_equips= array_values($llista_equips);

			return $ret[0]; 
		}
		
		public function generarHorari(){
			// Ordenem els partits en llista de prioritats i comencem a fer la inserció a l'horari
			//$matriu_aux = array();		// 2D[categoria][partits]
			$llista_aux = array();		// Igual que $matriu_aux, però sense distingir les categories
			
			$prior_participant = array(); // Matriu associativa de participants, amb els partits que ha de jugar
			$id_partit = 0;
			
			// Agafar tots els equips i crear tots les convinacions (partits)
			// Per cada cada convinació calcular les restriccions
			// Afegir a $matriu_aux el partit
			$n_grups = sizeof($this->grups);
			for ($i=0; $i < $n_grups; $i++) {
				
				$equips = $this->grups[$i]->getEquips();
				$n_equips = sizeof($equips);
				for ($j=0; $j < $n_equips; $j++) { 
					for ($k=$j+1; $k < $n_equips; $k++) {
						//Creem el partit
						$partit = new partit();
						$partit->setId($id_partit++);
						$partit->setEquips($equips[$j], $equips[$k]);
						$partit->setRestriccions($equips[$j]->getRes(), $equips[$k]->getRes());
						//$matriu_aux[$this->grups[$i]->getCategoria()][] = $partit;
						$llista_aux[] = $partit;
						
						//Per cada partit, augmentem la prioritat dels jugadors implicats
						$part1 = $equips[$j]->getPart();
						$n_part1 = sizeof($part1);
						for ($l=0; $l < $n_part1; $l++) {
							$prior_participant[$part1[$l]][] = $partit;
						}
						$part2 = $equips[$k]->getPart();
						$n_part2 = sizeof($part2);

						for ($l=0; $l < $n_part2; $l++) { 
							$prior_participant[$part2[$l]][] = $partit;
						}
					}
				}

			}
			
			// $matriu_aux és correcte, però he decidit no fer diferències per categories i 
			// englobar tots els partits en una mateixa llista, si en un futur es vol canviar
			// l'algoritsme, $matriu_aux conté els valors correctament calculats, en principi xD

			
			// Donem les primeres prioritats i ordenem per primer cop el vector
			// (NUMERO DE PRIORITAT MÉS GRAN, MÉS PRIORITAT)
			$partits_categ = array();
			$n_partits = sizeof($llista_aux);
			for ($i=0; $i < $n_partits; $i++) { 
				$this->determinarPrioritat($llista_aux[$i], $prior_participant);
				
				// Contem els partits de cada categoria
				if(isset($partits_categ[$llista_aux[$i]->getCat()])) 	$partits_categ[$llista_aux[$i]->getCat()]++;
				else  													$partits_categ[$llista_aux[$i]->getCat()] = 1;
			}
			$this->ordenarPartits($llista_aux);
	
			// Decidir prioritats pistes  (NUMERO MÉS PETIT, MÉS PRIORITAT)
			for ($i=0; $i < $this->n_pistes; $i++) { 
				$cats = $this->pistes[$i]->getCats();
				$p = 0;
				for ($j=0; $j < sizeof($cats); $j++) {
					if(isset($partits_categ[$cats[$j]])) 	$p += $partits_categ[$cats[$j]];
				}
				$this->pistes[$i]->setPrioritat($p);
			}
			// Ordenem les pistes per prioritat, tenen un id per restablir l'ordre inicial
			$this->ordenarPistes();

			// $llista_aux, $prior_participants, $this->pistes i $this->taulaHorari estan llestos per fer servir
			
			// TODO Comprobar abans de començar si hi ha espai real per posar els partits?
			
			// Començar la inserció de partits tenint en compte les prioritats dels partits i de les pistes
			// Inserir sempre de hora en hora (no deixar espais buits), respectant les prioritats
			
			// Recalcular prioritat i ordenar a cada iteració	
			// Recordar ultims dos, per evitar jugar partits seguits (pensar com fer-ho)		
			
			
			

			$part_cansats = array();/*new SplFixedArray(TEMPS_REPOS+1);	*/	// pos 0 = jugant, pos 1 = acaven de jugar, pos 2 = han descansat 1 partit... etc 
			for ($i=0; $i < TEMPS_REPOS+1; $i++) { 
				$part_cansats[$i] = array();
			}
			
			$p_dia = 0;
			$p_hora = 0;
			$p_pista = -1;
			$fi_horari = false;
			while(sizeof($llista_aux) > 0 && !$fi_horari){		// Queden partits per insertar
				echo "ITERACIO BUCLE <br><br>";
				$data = new data();
				$this->selSegPista($data, $pista, $p_dia, $p_hora, $p_pista, $fi_horari);	// Obtenim la data i la pista on buscar partits.
				echo "DIA: $p_dia, PISTA: " ./*$p_pista*/ $pista->getId().", HORA: $p_hora <br>";

                if(!$fi_horari)
                    $this->selMillorPartit($data, $pista, $llista_aux, $prior_participant, $part_cansats,  $p_dia, $p_hora, $p_pista);
																	// Buscarem el millor equip per la data i pista obtinguda,
																	//  tenint en compte els altres partits jugats i que s'estan
																	//  jugant i les prioritats. Un cop seleccionat i eliminat
																	//  de la llista, recalcular prioritat i reordenar 
																	//  ($llist_aux i $prior_participants).
															
																	// Inserim el partit a la data seleccionada,
																	//  després notifiquem que els jugadors estan jugant
																	//  per evitar que tornin a jugar tot seguit.
			}		

			echo "<br><br> HORARI FINALIZAT <br><br>";
			$this->imprimirHorariPantalla();
            if($fi_horari){
                // TODO, avisar al log!
                echo "<br><br>HORARI INCOMPLET, FALTEN HORES!!!<br><br>";
            }
		}

		private function selSegPista(&$data, &$pista, &$p_dia, &$p_hora, &$p_pista, &$fi_horari){
			
			$p_pista++;
			if($p_pista >= $this->n_pistes){
				$p_pista = 0;
				$p_hora++;
				if($p_hora >= sizeof($this->taulaHorari[$p_dia][$p_pista])){
					$p_hora = 0;
					$p_dia++;
					if($p_dia >= sizeof($this->taulaHorari)){
						// TODO avisar que no hi ha hores suficients!!
                        $fi_horari = true;
					}
				}
			}
			$h_partit = $this->hores_torneig[$p_dia]->getHoraIni()+($p_hora/$this->partits_hora);
			$data->setData($p_dia, $h_partit, $h_partit);
			$pista = $this->pistes[$p_pista];

			//echo "$p_dia $p_hora $p_pista -> $h_partit / $pista<br>"; 
		}
		
		private function selMillorPartit($data, $pista, array &$llista_partits, array &$prior_part, array &$part_cansats,  $p_dia, $p_hora, $p_pista){

			$millor_part = NULL;
			$penalitz_part = -1;
			
			$trobat = false;
			$n_partits = sizeof($llista_partits);
			for ($i=0; $i < $n_partits && !$trobat; $i++) { 
				$penalitz_aux = $this->analitzaPartit($llista_partits[$i], $data, $pista, $part_cansats);		// Mira si esta jugant o ha jugat fa poc
				if($penalitz_aux != -1 && ($penalitz_part == -1 || $penalitz_aux < $penalitz_part)){
					$penalitz_part = $penalitz_aux;
					$millor_part = $i;
					if($penalitz_part == 0)	$trobat = true;
				}
			}
			
			// AFEGIR PARTIT
			if(is_null($millor_part)){
				echo "NO SHA INSERIT!<br><br>";
				/*
				$p = new partit();
				$p->setId(-1);
				$this->taulaHorari[$p_dia][$pista->getId()][$p_hora] = $p;
				*/
			} else {
				echo "=======> PARTIT INSERIT!<br>";
				echo "PENALITZACIO CANSAMENT: $penalitz_part<br>";
				print_r($part_cansats);
                echo "<br><br>";
                echo $llista_partits[$millor_part]->getEquip1()->getNom(). " vs " . $llista_partits[$millor_part]->getEquip2()->getNom();
				echo "<br><br>";
				echo "$p_dia ". $pista->getId(). " $p_hora <br><br>";
				$this->taulaHorari[$p_dia][$pista->getId()][$p_hora] = clone $llista_partits[$millor_part];

			}
			
			// ACTUALITZAR ESTRUCTURES
			$ll_dism_prior = array();	// Llista de partits que disminuirem la prioritat
			if(!is_null($millor_part)){
				$parts = $llista_partits[$millor_part]->getPart();
				
				for ($i=0; $i < sizeof($parts); $i++) {
					array_push($part_cansats[0], $parts[$i]);	// L'afegim a la llista de jugant / cansats
					
					$n = -1;
					for ($j=0; $j < sizeof($prior_part[$parts[$i]]); $j++) { 	// Eliminem el partit de $prior_part
						if($llista_partits[$millor_part]->getId() == $prior_part[$parts[$i]][$j]->getId())	$n = $j;
						else {
							$ll_dism_prior[$prior_part[$parts[$i]][$j]->getId()] = $prior_part[$parts[$i]][$j];
						}
						
					}
					if($n != -1){
						unset($prior_part[$parts[$i]][$n]);
						$prior_part[$parts[$i]] = array_values($prior_part[$parts[$i]]);
					}
				}				
				echo "LL_DISM_PRIOR<br>";
				print_r($ll_dism_prior);
				echo "<br><br>";
				
				// TODO la prioritat no només son partits per jugar! tm son les restriccions de l'equip!
				
				foreach ($ll_dism_prior as $partit => $p) {		// Reduim les prioritats
				
					$this->determinarPrioritat($p, $prior_part);
				/*
					$parts = $p->getPart();
					$max = 0;
					for ($i=0; $i < sizeof($parts) ; $i++) { 
						$n = sizeof($prior_part[$parts[$i]]);
						if($n > $max)	$max = $n;
					}
					$p->setPrioritat($max);
				 * */
				}	
				 

				// Eliminem el partit inserit, de la llista de partits
				unset($llista_partits[$millor_part]);
				$llista_partits = array_values($llista_partits);	 
				 	
				$this->ordenarPartits($llista_partits);				// Reordenem llista de partits per prioritats
					
				


			} 
			if($p_pista == $this->n_pistes-1){			// Estem amb l'últim partit de la hora, s'ha de rotar
															// la llista de gent jugant / cansada
				for ($i=TEMPS_REPOS-1; $i >= 0; $i--) { 
					$part_cansats[$i+1] = $part_cansats[$i];
				}
				$part_cansats[0] = array();
				
			}

		}
		
		// Retornem la prioritat per jugar: 0 partit perfecte.. -1 no es pot jugar.. 1, 2... menys prioritat
		// TODO Comprobar el funcionament de les restriccions
		private function analitzaPartit($partit, $data, $pista, $part_cansats){
			echo "PISTA<br>";
			print_r($pista->getCats());
			echo "<br><br>PARTIT <br>";
			print_r($partit);
			echo "<br><br>";
			$cats = $pista->getCats();
			$trobat = FALSE;
			for ($i=0; $i < sizeof($cats) && !$trobat; $i++) { 
				if($cats[$i] == $partit->getCat())	$trobat = TRUE;
			}
			if(!$trobat){
				echo "--- El partit no es de la categoria de la pista<br><br>";
					return -1;				// Partit que no és de la categoria
			}
			
			$part = $partit->getPart();
			/*

			for ($i=0; $i < sizeof($part); $i++) {
				
				$trobat = FALSE; 
				for ($j=0; $j < sizeof($this->participants) && !$trobat; $j++) { 
					if($this->participants[$j]->getDni() == $part[$i]){
						$trobat = TRUE;
						if(!$this->participants[$j]->potJugar($data)){
							echo "$part[$i] NO POT JUGAR PER RESTRICCIONS!!! <br><br>";
								return -1;	// El jugador no pot jugar per restriccions horaries
						}
					}
				}
			}
			*/
			if(!$partit->esPotJugar($data)){
				echo "--- NO ES POT JUGAR PER RESTRICCIONS!!! <br><br>";
				return -1;	// El partit no es pot jugar per restriccions horaries
			}
			
			
			
			$ret = 0;
			for ($i=0; $i < sizeof($part_cansats); $i++) { 
				for ($j=0; $j < sizeof($part_cansats[$i]); $j++) { 
					for ($k=0; $k < sizeof($part); $k++) {
												
						if($part[$k] == $part_cansats[$i][$j]){
							// TODO son objectes o noms simplement? jo crec k noms oi..?
							if($i == 0){
								echo "--- Algun jugador esta en alguna altre categoria<br><br>";
								return -1;	// No es pot jugar pq algun participant ja esta jugant
							}
							$ret = $i;
						}
					}
				}
			}
			return $ret;
		}
		
		// Els objectes de $llista han de ser de tipus partit! (Descendent)
		private function ordenarPartits(array &$llista){
			if(!function_exists("cmp")){
				function cmp($a, $b){
					return $b->getPrioritat() - $a->getPrioritat();
				}
			}
			usort($llista, "cmp");
		}
		
		// Ascendent
		private function ordenarPistes(){
			if(!function_exists("cmp2")){
				function cmp2($a, $b){
					return $a->getPrioritat() - $b->getPrioritat();
				}
			}

			usort($this->pistes, "cmp2");
		}
		
		private function determinarPrioritat(&$partit, array $pr){
			$prior_part = 0;
			
			$part = $partit->getPart();
			$n_part = sizeof($part);
			$max = 0;
			
			for ($i=0; $i < $n_part; $i++) { 
				$aux = sizeof($pr[$part[$i]]);
				if($aux > $max)	$max = $aux;
			}

			$p = $partit->getNumRes() + $max;
			$partit->setPrioritat($p);
			
						echo "DETERMINAR PRIORITAT <br><br>";
			print_r($partit);
			echo "<br> Num Prioritat: $p <br><br>";
		}
		
		
		private function imprimirHorariPantalla(){
			for ($i=0; $i < sizeof($this->taulaHorari); $i++) { 
				echo "----- DIA $i<br>";
				for ($j=0; $j < sizeof($this->taulaHorari[$i]); $j++) { 
					echo "--- PISTA $j<br>";
					for ($k=0; $k < sizeof($this->taulaHorari[$i][$j]); $k++) {
						if($this->taulaHorari[$i][$j][$k]->getId() != -1){ 
							echo "- HORA $k: " . $this->taulaHorari[$i][$j][$k]->getEquip1()->getNom() . " vs " .
										  	  	  $this->taulaHorari[$i][$j][$k]->getEquip2()->getNom() . "<br>";
						} else {
							echo "- HORA $k: -----<br>";
						}
					}
				}
			}
			
		}
		
		
    }












?>